package com.disruptor.demo.handler;

import com.disruptor.demo.event.SeckillOrderEvent;
import com.lmax.disruptor.EventHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 结果通知处理器：Disruptor消费链的最终处理器（第三步）
 * 依赖关系：必须在OrderProcessHandler（订单处理）完成后执行
 * 核心职责：
 * 1. 向用户推送订单处理结果（成功/失败），是秒杀流程的收尾环节
 * 2. 通过批量日志减少IO开销，避免单条通知日志刷屏
 * 3. 提供通知处理完成的判断逻辑，确保统计时所有通知已处理完毕
 */
@Slf4j
@Component
public class ResultNotifyHandler implements EventHandler<SeckillOrderEvent> {
    /**
     * 累计成功通知数
     * 原子类保证线程安全：支持多线程消费场景下的计数准确性（Disruptor可能配置多消费者线程）
     */
    private final AtomicInteger successNotifyCount = new AtomicInteger(0);

    /**
     * 累计失败通知数
     * 包含两种失败：前序步骤（库存/订单）失败导致的通知失败、通知自身异常导致的失败
     */
    private final AtomicInteger failNotifyCount = new AtomicInteger(0);

    /**
     * 当前批次通知数
     * 用于批量日志计数，达到阈值后打印汇总日志，平衡日志详细度与性能
     */
    private final AtomicInteger batchNotifyCount = new AtomicInteger(0);

    /**
     * 上一次总通知数（成功+失败）
     * volatile修饰保证可见性：供主线程判断通知是否稳定（不再变化）
     * 作用：标记通知处理的"快照"，用于检测是否还有新通知在处理
     */
    private volatile int lastTotalNotifyCount = 0;

    /**
     * 批量日志阈值：每处理1000条通知或达到事件批次末尾时，打印一次汇总日志
     * 阈值设计：10000条/批既保证日志不会过于零散，又能及时反映通知进度
     */
    private static final int BATCH_LOG_THRESHOLD = 200 * 10000;

    /**
     * 秒杀商品最大库存
     * 用途：作为订单成功数的上限（验证成功订单数≤maxStock，确保无超卖），默认100
     */
    @Value("${seckill.max-stock:100}")
    private int maxStock;

    /**
     * Disruptor事件处理核心方法：处理订单通知逻辑
     * 调用时机：当RingBuffer中有新事件，且前序处理器（OrderProcessHandler）完成后触发
     *
     * @param event      待通知的秒杀订单事件（包含最终处理结果）
     * @param sequence   事件在RingBuffer中的唯一序列号（用于追踪事件位置）
     * @param endOfBatch 是否为当前批次的最后一个事件（确保末尾事件能触发汇总日志）
     */
    @Override
    public void onEvent(SeckillOrderEvent event, long sequence, boolean endOfBatch) throws Exception {
        // 1. 更新事件处理状态：标记为"NOTIFY"，标识进入通知阶段
        event.setProcessStatus("NOTIFY");

        try {
            // 2. 统计通知结果：根据前序处理结果（errorMsg）区分成功/失败通知
            if (event.getErrorMsg() == null) {
                // 前序处理成功（无错误信息）→ 通知成功计数+1
                successNotifyCount.incrementAndGet();
                if (successNotifyCount.get() == maxStock) {
                    log.info("【消息通知】successNotifyCount等于maxStock，已完成最后一个秒杀名额的消息通知，即该商品已被秒杀完毕");
                }
            } else {
                // 前序处理失败（有错误信息）→ 通知失败计数+1
                failNotifyCount.incrementAndGet();
            }
            // 3. 更新批次计数：当前批次通知数+1
            batchNotifyCount.incrementAndGet();

            // 4. 批量打印汇总日志：达到阈值或批次末尾时触发
            // 结合endOfBatch确保最后一批未达阈值的事件也能输出日志，避免统计遗漏
//            if (batchNotifyCount.get() >= BATCH_LOG_THRESHOLD || endOfBatch) {
//                log.info("【通知批次汇总】本批次通知：{}条，累计成功：{}条，累计失败：{}条",
//                        batchNotifyCount.get(), successNotifyCount.get(), failNotifyCount.get());
//                batchNotifyCount.set(0); // 重置批次计数，准备下一批统计
//            }
            // fixme endOfBatch高频触发问题
            //  Disruptor 的BatchEventProcessor默认按 “RingBuffer 大小的 1/8” 或 “当前可用事件数” 处理批次
            //  （比如 RingBuffer=16384 时，批次大小 = 2048）。但在 1000 万请求的高并发场景下，RingBuffer 会被 “持续填满 - 消费”，
            //  导致BatchEventProcessor频繁处理 “不完整小批次”（如 94 条），每次处理完都触发endOfBatch=true。
            if (batchNotifyCount.get() >= BATCH_LOG_THRESHOLD) {
                log.info("【通知批次汇总】本批次通知：{}条，累计成功：{}条，累计失败：{}条",
                        batchNotifyCount.get(), successNotifyCount.get(), failNotifyCount.get());
                batchNotifyCount.set(0); // 重置批次计数，准备下一批统计
            }

            // 5. 更新总通知数快照：供主线程判断通知是否稳定（不再变化）
            lastTotalNotifyCount = successNotifyCount.get() + failNotifyCount.get();

        } catch (Exception e) {
            // 6. 通知自身异常处理（如推送接口调用失败）
            log.error("【通知异常】orderId={}，原因：{}", event.getOrderId(), e.getMessage(), e);
            // 异常情况计入失败通知（确保总通知数与订单数一致）
            failNotifyCount.incrementAndGet();
            // 同步更新总通知数快照
            lastTotalNotifyCount = successNotifyCount.get() + failNotifyCount.get();
        }
    }

    /**
     * 判断通知是否处理完成（稳定状态）
     * 核心逻辑：连续两次检查（间隔500ms）总通知数不变，说明所有通知已处理完毕
     * 设计目的：解决主线程与Disruptor消费者线程的异步问题，确保统计时无未完成的通知
     *
     * @return true：通知已稳定（处理完成）；false：仍有通知在处理
     */
    public boolean isNotifyStable() {
        // 1. 第一次检查：当前总通知数是否与上一次快照一致
        int currentTotal = successNotifyCount.get() + failNotifyCount.get();
        if (currentTotal != lastTotalNotifyCount) {
            return false; // 计数仍在变化，未稳定
        }

        // 2. 等待500ms后二次确认：避免短时间内计数不变的"假稳定"
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 保留中断状态，不吞掉异常
        }

        // 3. 二次检查：确认计数确实不再变化
        return currentTotal == (successNotifyCount.get() + failNotifyCount.get());
    }

    /**
     * 获取累计成功通知数
     * 用途：供测试类统计最终结果，验证成功通知数是否与成功订单数一致
     */
    public int getSuccessNotifyCount() {
        return successNotifyCount.get();
    }

    /**
     * 获取累计失败通知数
     * 用途：供测试类统计最终结果，验证失败通知数是否与失败订单数一致
     */
    public int getFailNotifyCount() {
        return failNotifyCount.get();
    }

    /**
     * 获取总通知数（成功+失败）
     * 用途：验证总通知数是否与总订单数一致（确保无通知遗漏）
     */
    public int getTotalNotifyCount() {
        return successNotifyCount.get() + failNotifyCount.get();
    }
}
